﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using IgnData.TileData;
using IgnData.TileData.IGNtilesDataSetTableAdapters;
using System.Net;
using System.IO;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Configuration;
using System.Data.Common;
using System.Data;

namespace IgnData
{
    public static class TileManager
    {
        private static TraceSource logger = new TraceSource("IgnData");

        private static UriBuilder uriBuilder = new UriBuilder("http://ts0.iberpix.ign.es");
        private static string path = "/tileserver/n=mapa_mtn25;z=30;r=4000;i={0};j={1}.jpg";

        private static IGN25TableAdapter ign25TA = new IGN25TableAdapter();
        private static readonly string PROVIDER_NAME = ConfigurationManager.ConnectionStrings["DirectIGNTilesConnection"].ProviderName;
        private static readonly string CONNECTION_STRING = ConfigurationManager.ConnectionStrings["DirectIGNTilesConnection"].ConnectionString;
        private static DbProviderFactory factory = DbProviderFactories.GetFactory(PROVIDER_NAME);

        public static Tile GetTile(int i, int j)
        {

            if (IsTilePresentBBDD(i, j))
            {
                return GetTileFromDDBBDirect(i, j);
            }
            else
            {
                Tile til = GetTileFromIGN(i, j);
                SaveTileInDDBBDir(til);
                return til;
            }

        }

        private static Tile GetTileFromDDBB(int i, int j)
        {
            try
            {
                DateTime start = DateTime.Now;
                IGNtilesDataSet.IGN25DataTable dtIGN25 = ign25TA.GetDataByCoord(i, j);
                if (dtIGN25.Rows.Count == 1)
                {
                    Bitmap bmp = null;
                    using (MemoryStream ms = new MemoryStream(dtIGN25[0].TILE))
                    {
                        bmp = new Bitmap(ms);
                    }

                    logger.TraceInformation("Tile ({0},{1}) from DDBB: {2}", i, j, DateTime.Now - start);
                    return new Tile(i, j, bmp);
                }
                else
                {
                    return null;
                }
            }
            catch (Exception e)
            {
                logger.TraceEvent(TraceEventType.Error, 0, "GetTileFromDDBB() {0}", e.Message);
                throw;
            }
        }

        private static void SaveTileInDDBB(Tile til)
        {
            try
            {
                if (til == null) return;
                IGNtilesDataSet.IGN25DataTable dtIGN25 = new IGNtilesDataSet.IGN25DataTable();
                IGNtilesDataSet.IGN25Row tr = dtIGN25.NewIGN25Row();
                tr.COORD_X = til.Coord_I;
                tr.COORD_Y = til.Coord_J;
                using (MemoryStream ms = new MemoryStream())
                {
                    til.Bmp.Save(ms, ImageFormat.Jpeg);
                    byte[] bitmapData = ms.ToArray();
                    tr.TILE = bitmapData;
                }
                tr.DATE = DateTime.Now;
                dtIGN25.AddIGN25Row(tr);
                ign25TA.Update(dtIGN25);
            }
            catch (Exception e)
            {
                logger.TraceEvent(TraceEventType.Error, 0, "SaveTileInDDBB() {0}", e.Message);
                throw;
            }
        }

        private static Tile GetTileFromIGN(int i, int j)
        {
            DateTime start = DateTime.Now;
            try
            {
                Bitmap bmp = null;
                uriBuilder.Path = string.Format(path, i, j);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
                bmp = (Bitmap)Bitmap.FromStream(request.GetResponse().GetResponseStream());
                logger.TraceInformation("Tile ({0},{1}) from IGN: {2}", i, j, DateTime.Now - start);
                return new Tile(i, j, bmp);
            }
            catch (Exception e)
            {
                logger.TraceEvent(TraceEventType.Error, 0, "GetTileFromIGN() {0}", e.Message);
                throw;
            }
        }

        private static Tile GetTileFromDDBBDirect(int i, int j)
        {
            try
            {
                DateTime start = DateTime.Now;
                using (DbConnection connection = factory.CreateConnection())
                {
                    connection.ConnectionString = CONNECTION_STRING;

                    using (DbCommand command = factory.CreateCommand())
                    {
                        command.Connection = connection;
                        command.CommandType = CommandType.Text;
                        command.CommandText =
                            @"SELECT     COORD_X, COORD_Y, TILE
                            FROM         IGN25
                            WHERE     (COORD_X = @i) AND (COORD_Y = @j)";
                        DbParameter p1 = command.CreateParameter();
                        p1.ParameterName = "i";
                        p1.Value = i;
                        command.Parameters.Add(p1);
                        DbParameter p2 = command.CreateParameter();
                        p2.ParameterName = "j";
                        p2.Value = j;
                        command.Parameters.Add(p2);

                        connection.Open();
                        using (DbDataReader reader = command.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                int x = (int)reader["COORD_X"];
                                int y = (int)reader["COORD_y"];
                                byte[] tile = (byte[])reader["TILE"];
                                Bitmap bmp = null;
                                using (MemoryStream ms = new MemoryStream(tile))
                                {
                                    bmp = (Bitmap)Bitmap.FromStream(ms);
                                }
                                logger.TraceInformation("Tile ({0},{1}) from DDBBdir: {2}", i, j, DateTime.Now - start);
                                return new Tile(i, j, bmp);
                            }
                            else
                            {
                                return null;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                logger.TraceEvent(TraceEventType.Error, 0, "GetTileFromDDBBDirect() {0}", e.Message);
                throw;
            }

        }

        private static void SaveTileInDDBBDir(Tile til)
        {
            try
            {
                DateTime start = DateTime.Now;
                using (DbConnection connection = factory.CreateConnection())
                {
                    connection.ConnectionString = CONNECTION_STRING;
                    using (DbCommand command = factory.CreateCommand())
                    {
                        command.Connection = connection;
                        command.CommandType = CommandType.Text;
                        command.CommandText =
                            @"INSERT INTO IGN25  (COORD_X, COORD_Y, TILE, DATE)  VALUES ( @i, @j, @tile, @date)";
                        DbParameter p1 = command.CreateParameter();
                        p1.ParameterName = "i";
                        p1.Value = til.Coord_I;
                        command.Parameters.Add(p1);
                        DbParameter p2 = command.CreateParameter();
                        p2.ParameterName = "j";
                        p2.Value = til.Coord_J;
                        command.Parameters.Add(p2);
                        DbParameter p3 = command.CreateParameter();
                        p3.ParameterName = "tile";
                        using (MemoryStream ms = new MemoryStream())
                        {
                            til.Bmp.Save(ms, ImageFormat.Jpeg);
                            byte[] bitmapData = ms.ToArray();
                            p3.Value = bitmapData;
                        }
                        command.Parameters.Add(p3);
                        DbParameter p4 = command.CreateParameter();
                        p4.ParameterName = "date";
                        p4.Value = DateTime.Now;
                        command.Parameters.Add(p4);

                        connection.Open();
                        int n = command.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception e) 
            {
                logger.TraceEvent(TraceEventType.Error, 0, "SaveTileInDDBBDir() {0}", e.Message);
                throw;
            }
        }

        private static bool IsTilePresentBBDD(int i, int j)
        {
            try
            {
                using (DbConnection connection = factory.CreateConnection())
                {
                    connection.ConnectionString = CONNECTION_STRING;
                    using (DbCommand command = factory.CreateCommand())
                    {
                        command.Connection = connection;
                        command.CommandType = CommandType.Text;
                        command.CommandText =
                            @"SELECT COUNT(1) FROM    IGN25 WHERE (COORD_X = @i) AND (COORD_Y = @j)";
                        DbParameter p1 = command.CreateParameter();
                        p1.ParameterName = "i";
                        p1.Value = i;
                        command.Parameters.Add(p1);
                        DbParameter p2 = command.CreateParameter();
                        p2.ParameterName = "j";
                        p2.Value = j;
                        command.Parameters.Add(p2);

                        connection.Open();
                        int nt = (int)command.ExecuteScalar();
                        return nt == 1;
                    }
                }
            }
            catch (Exception e)
            {

                throw e;
            }

        }
    }
}